// 
// Copyright (c) Microsoft and contributors.  All rights reserved.
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// 
// See the License for the specific language governing permissions and
// limitations under the License.
// 

// Test includes
var fs = require('fs');
var sinon = require('sinon');

var nockHelper = require('./nock-helper');
var KvTestUtils = require('../services/keyVault/kv-test-utils');

exports = module.exports = MockedTestUtils;

function MockedTestUtils(service, testPrefix) {
  this.service = service;
  this.testPrefix = testPrefix;
  this.mockServerClient;
  this.currentTest = 0;
  this.scopeWritten;
  this.recordingsFile = __dirname + '/../recordings/' + this.testPrefix + '.nock.js';
  this.isMocked = !process.env.NOCK_OFF;
  this.isRecording = process.env.AZURE_NOCK_RECORD;
  this.isPlayback = !process.env.NOCK_OFF && !process.env.AZURE_NOCK_RECORD;

  this.setupService(service);
  this.stubMethods();
}

MockedTestUtils.prototype.setupService = function (service) {
  // Disable strict SSL in playback mode
  if (this.isMocked && !this.isRecording) {
    service.strictSSL = false;
  }
};

MockedTestUtils.prototype.setupSuite = function (callback) {
  nockHelper.nockHttp();

  if (this.isMocked && this.isRecording) {
    fs.writeFileSync(this.recordingsFile, 
      '// This file has been autogenerated.\n\n' +
      'exports.scopes = [');
  }

  callback();
};

MockedTestUtils.prototype.teardownSuite = function (callback) {
  this.currentTest = 0;

  if (this.isMocked && this.isRecording) {
    fs.appendFileSync(this.recordingsFile, '];');
  }

  nockHelper.unNockHttp();

  callback();
};

MockedTestUtils.prototype.setupTest = function (callback) {
  if (this.isMocked && this.isRecording) {
    // nock recoding
    nockHelper.nock.recorder.rec(true);
  } else if (!process.env.NOCK_OFF) {
    // nock playback
    var nocked = require(this.recordingsFile);

    if (this.currentTest < nocked.scopes.length) {
      nocked.scopes[this.currentTest++].forEach(function (createScopeFunc) {
        createScopeFunc(nockHelper.nock);
      });
    } else {
      throw new Error('It appears the ' + this.recordingsFile + ' file has more tests than there are mocked tests. '
        + 'You may need to re-generate it.');
    }
  }

  callback();
};

MockedTestUtils.prototype.baseTeardownTest = function (callback) {
  if (this.isMocked && this.isRecording) {
    // play nock recording
    var scope = this.scopeWritten ? ',\n[' : '[';
    this.scopeWritten = true;
    var lineWritten;
    nockHelper.nock.recorder.play().forEach(function (line) {
      if (line.indexOf('nock') >= 0) {
        // apply fixups of nock generated mocks

        // do not filter on body as they usual have time related stamps
        line = line.replace(/(\.post\('.*')[^\)]+\)/, '.filteringRequestBody(function (path) { return \'*\';})\n$1, \'*\')');
        line = line.replace(/(\.get\('.*')[^\)]+\)/, '.filteringRequestBody(function (path) { return \'*\';})\n$1, \'*\')');
        line = line.replace(/(\.put\('.*')[^\)]+\)/, '.filteringRequestBody(function (path) { return \'*\';})\n$1, \'*\')');
        line = line.replace(/(\.delete\('.*')[^\)]+\)/, '.filteringRequestBody(function (path) { return \'*\';})\n$1, \'*\')');
        line = line.replace(/(\.merge\('.*')[^\)]+\)/, '.filteringRequestBody(function (path) { return \'*\';})\n$1, \'*\')');
        line = line.replace(/(\.patch\('.*')[^\)]+\)/, '.filteringRequestBody(function (path) { return \'*\';})\n$1, \'*\')');

        // put deployment have a timestamp in the url
        line = line.replace(/(\.put\('\/deployment-templates\/\d{8}T\d{6}')/,
          '.filteringPath(/\\/deployment-templates\\/\\d{8}T\\d{6}/, \'/deployment-templates/timestamp\')\n.put(\'/deployment-templates/timestamp\'');

        // Requests to logging service contain timestamps in url query params, filter them out too
        line = line.replace(/(\.get\('.*\/microsoft.insights\/eventtypes\/management\/values\?api-version=[0-9-]+)[^)]+\)/,
          '.filteringPath(function (path) { return path.slice(0, path.indexOf(\'&\')); })\n$1\')');
        if (line.match(/\/oauth2\/token\//ig) === null &&
          line.match(/login\.windows\.net/ig) === null && 
          line.match(/login\.windows-ppe\.net/ig) === null &&
          line.match(/login\.microsoftonline\.com/ig) === null &&
          line.match(/login\.chinacloudapi\.cn/ig) === null &&
          line.match(/login\.microsoftonline\.de/ig) === null) {
          scope += (lineWritten ? ',\n' : '') + 'function (nock) { \n' +
            'var result = ' + line + ' return result; }';
          lineWritten = true;
        }
      }
    });
    scope += ']';
    fs.appendFileSync(this.recordingsFile, scope);
    nockHelper.nock.recorder.clear();
    nockHelper.nock.restore();
  }

  callback();
};

/**
 * Stubs certain methods to make them work in playback mode.
 */
MockedTestUtils.prototype.stubMethods = function() {
  if (this.isPlayback) {
    if (KvTestUtils.authenticator.restore) {
      KvTestUtils.authenticator.restore();
    }
    sinon.stub(KvTestUtils, 'authenticator').callsFake(function(challenge, callback) {
      return callback(null, 'Bearer MockedTest');
    });
  }
}